using System;
using System.Collections.Generic;
using System.IO;

namespace Subsync.Core.Sync
{
	/// <summary>
	/// Author: Steven Goh Jian Wen, except the methods with Author: Hoang Duc
	/// </summary>
	public class CLIManager : App
	{
		public override Dictionary<string, List<object>> GetTokens ()
		{
			Dictionary<string, List<object>> ret = new Dictionary<string, List<object>> ();
			
			//For syncing
			ret.Add("new_sync",new List<object>() {"n","newsync"});
			ret.Add("add_sync",new List<object>() {"a","addsync"});
			ret.Add("add_local_sync",new List<object>() {"al","addlocalsync"});
			ret.Add("sync",new List<object>() {"s","sync"});
            ret.Add("backupTransverse", new List<object>() { "tt", "timetravel" });
            ret.Add("removeSync", new List<object>() { "d", "deletesync" });
			ret.Add("help", new List<object>() { "h", "help" });
			
			//sync: for additional args
			ret.Add("custom_repo_dir",new List<object>() {"customrepo","cr"});
			ret.Add("description",new List<object>() {"description","ds"});
			ret.Add("icon",new List<object>() {"icon","ic"});
			
			//For Config testing
			if (MainClass.debug) {
				ret.Add("new_config",new List<object>() {"newconfig"});
				ret.Add("fake_sync_save",new List<object>() {"fakesyncsave"});
				ret.Add("fake_sync_del",new List<object>() {"fakesyncdel"});
				ret.Add("fake_sync_load",new List<object>() {"fakesyncload"});
			}
			return ret;
		}
		
		public override List<KeyValuePair<string, initPointer>> GetInitPointers ()
		{
			List<KeyValuePair<string, initPointer>> ret = new List<KeyValuePair<string, initPointer>> ();
			
			//For syncing
			ret.Add (new KeyValuePair<string, initPointer> ("new_sync", new initPointer (this.SetupRepos)));
			ret.Add (new KeyValuePair<string, initPointer> ("add_local_sync", new initPointer (this.addLocalFoldertoSync)));
			ret.Add (new KeyValuePair<string, initPointer> ("add_sync", new initPointer (this.addNetworkURLtoSync)));
			ret.Add (new KeyValuePair<string, initPointer> ("sync", new initPointer (this.executeSync)));
			ret.Add (new KeyValuePair<string, initPointer> ("backupTransverse", new initPointer (this.executeBackupTransverse)));
			ret.Add (new KeyValuePair<string, initPointer> ("removeSync", new initPointer (this.executeRemove)));
			ret.Add(new KeyValuePair<string, initPointer>("help", new initPointer(this.Help)));
			//For Config testing
			if (MainClass.debug) {
				ret.Add (new KeyValuePair<string, initPointer> ("new_config", new initPointer (this.NewConfig)));
				ret.Add (new KeyValuePair<string, initPointer> ("fake_sync_save", new initPointer (this.FakeSyncSave)));
				ret.Add (new KeyValuePair<string, initPointer> ("fake_sync_del", new initPointer (this.FakeSyncDel)));
				ret.Add (new KeyValuePair<string, initPointer> ("fake_sync_load", new initPointer (this.FakeSyncLoad)));
			}
			return ret;
		}

		/// <summary>
		/// Usage: subsync.exe --newsync/-n <path_to_main_folder_to_be_synced> <sync-name: mp31> <(OPTIONAL) auto-manage-conflicts?: yes/no>
		/// </summary>
		/// <param name="tokens">
		/// A <see cref="Dictionary<System.String, List<System.Object>>"/>
		/// </param>
		public void SetupRepos (Dictionary<string, List<object>> tokens)
		{
			Sync s = new Sync ();
			
			string sync_name = "";
			string working_copy_path = "";
			bool auto_conflicts = true;
			bool use_cr = false;
			string custom_repo = "";
			
			if ((tokens["new_sync"].Count == 2) || (tokens["new_sync"].Count == 3)) {
				sync_name = tokens["new_sync"][1].ToString ();
				working_copy_path = tokens["new_sync"][0].ToString ();
				if (tokens["new_sync"].Count == 3)
					auto_conflicts = Tools.StringToBool (tokens["new_sync"][2].ToString ());
			}
            else {
				Console.WriteLine("Usage: subsync.exe --newsync/-n <path_to_main_folder_to_be_synced> <sync-name: mp31> <(OPTIONAL) auto-manage-conflicts?: yes/no>");
				return;
			}
			
			if (SyncExists(sync_name)) {
				Console.WriteLine("There exists a sync of this name, please try another name.");
				return;
			}

			if (!ValidPath(working_copy_path)) {
				Console.WriteLine("Invalid path provided.");
				return;
			}
			
			if ((tokens.ContainsKey("custom_repo_dir")) && (tokens["custom_repo_dir"].Count == 1)) {
				//there is a custom repository dir provided
				custom_repo = tokens["custom_repo_dir"][0].ToString();
                if (!Directory.Exists(custom_repo))
                {
                    Console.WriteLine("Invalid Custom Repository Path.");
                    return;
                }
                else
                {
                    string[] dirs = System.IO.Directory.GetDirectories(custom_repo);
                    string[] files = System.IO.Directory.GetFiles(custom_repo);

                    if (dirs.Length != 0 || files.Length != 0)
                    {
                        Console.WriteLine("Custom Repository not Empty");
                        return;
                    }                    
                }
				use_cr = true;
			}
			
			if ((tokens.ContainsKey("icon")) && (tokens["icon"].Count == 1)) {
				s.icon = tokens["icon"][0].ToString();
				if (!File.Exists(s.icon)) {
					Console.WriteLine("Icon image does not exist.");
					return;
				}
			}
			
			if ((tokens.ContainsKey("description")) && (tokens["description"].Count == 1))
				s.description = tokens["description"][0].ToString();
			
			s.name = sync_name;
			s.repo_url = s.working_copy_path = working_copy_path;
			s.auto_conflict_management = auto_conflicts;

            try
            {
                if (use_cr)
                    s.NewSync(custom_repo);
                else
                    s.NewSync();
            }
            catch (FolderDoesNotExist)
            {
                Console.WriteLine("Folder path does not exist");
                return;
            }
            catch (PathAlreadyAWorkingCopy)
            {
                Console.WriteLine("Path already a working copy");
                return;
            }
            catch (DiskSpaceException)
            {
                Console.WriteLine("Not enough disk space");
                return;
            }
            catch (UnauthorizedAccessException)
            {
                Console.WriteLine("Access denied");
                return;
            }
            catch (Exception)
            {
                Console.WriteLine("An unknown error has occurred.");
                return;
            }
			Console.WriteLine("Successfully created new sync ({0})",sync_name);
		}
		
		/// <summary>
		/// Usage: subsync.exe -al/--addlocalsync <path_to_add> <sync_name_to_be_synced> <sync_name_for_this_folder> <(OPTIONAL) auto_manage_conflicts: yes/no>
		/// </summary>
		/// <param name="tokens">
		/// A <see cref="Dictionary<System.String, List<System.Object>>"/>
		/// </param>
		public void addLocalFoldertoSync (Dictionary<string, List<object>> tokens)
		{
			string sync_name = "";
			string new_sync_name = "";
			string path_to_folder = "";
			bool auto_manage_conflicts = true;
			
			if ((tokens["add_local_sync"].Count < 3) || (tokens["add_local_sync"].Count > 4))
			{
				Console.WriteLine ("Usage: subsync.exe -al/--addlocalsync <path_to_add> <sync_name_to_be_synced> <sync_name_for_this_folder> <(OPTIONAL) auto_manage_conflicts: yes/no>");
				return;
			}
			
			sync_name = tokens["add_local_sync"][1].ToString ();
			new_sync_name = tokens["add_local_sync"][2].ToString ();
			path_to_folder = tokens["add_local_sync"][0].ToString();
			if (tokens["add_local_sync"].Count == 4) {
				if (ValidBool(tokens["add_local_sync"][3].ToString ()))	
					auto_manage_conflicts = Tools.StringToBool(tokens["add_local_sync"][3].ToString ());
				else {
					Console.WriteLine("Please use either yes/no for auto managing conflicts");
					return;
				}
			}

			if (!SyncExists(sync_name)) {
				Console.WriteLine("The sync you provided does not exist, perhaps you might want to create a sync first?");
				return;
			}
			if (SyncExists(new_sync_name)) {
				Console.WriteLine("The new sync name you provided already exists for another Sync, please try again.");
				return;
			}
			if (!ValidPath(path_to_folder)) {
				Console.WriteLine("The path you provided is not valid, please try again.");
				return;
			}

            try
            {
                new Sync().AddSync(SyncType.Local, sync_name, new_sync_name, path_to_folder, "", auto_manage_conflicts);
            }
            catch (PathAlreadyAWorkingCopy)
            {
                Console.WriteLine("Path already a working copy");
                return;
            }
            catch (DiskSpaceException)
            {
                Console.WriteLine("Not enough disk space");
                return;
            }
            catch (UnauthorizedAccessException)
            {
                Console.WriteLine("Access denied");
                return;
            }
            catch (Exception)
            {
                Console.WriteLine("An unknown error has occurred.");
                return;
            }
            Console.WriteLine ("Successfully added {0} to {1} sync.", path_to_folder, sync_name);
		}
		
		/// <summary>
		/// Usage: subsync.exe -a/-addsync <path_to_sync> <url of repos> <new-name-for-this-sync> <auto_manage_conflicts: yes/no>
		/// </summary>
		/// <param name="tokens">
		/// A <see cref="Dictionary<System.String, List<System.Object>>"/>
		/// </param>
		public void addNetworkURLtoSync (Dictionary<string, List<object>> tokens)
		{
			string path_to_folder = "";
			string repos_url = "";
			string new_sync_name = "";
			bool auto_manage_conflicts = true;
			
			if ((tokens["add_sync"].Count < 3) || (tokens["add_sync"].Count > 4))
			{
				Console.WriteLine ("Usage: subsync.exe -a/-addsync <path_to_sync> <url of repos> <new-name-for-this-sync> <auto_manage_conflicts: yes/no>");
				return;
			}
			
			path_to_folder = tokens["add_sync"][0].ToString();
			repos_url = tokens["add_sync"][1].ToString();
			new_sync_name = tokens["add_sync"][2].ToString();
			if (tokens["add_sync"].Count == 4) {
				if (ValidBool(tokens["add_sync"][3].ToString())) 
					auto_manage_conflicts = Tools.StringToBool(tokens["add_sync"][3].ToString());
				else {
					Console.WriteLine("Please use only yes/no for conflict management switch.");
					return;
				}
			}
			
			if (!ValidPath(path_to_folder)) {
				Console.WriteLine("You have provided an invalid path, please try again.");
				return;
			}
			if (SyncExists(new_sync_name)) {
				Console.WriteLine("The sync name you provided already exists, please use another.");
				return;
			}

            try
            {
                new Sync().AddSync(SyncType.Network, "", new_sync_name, path_to_folder, repos_url, auto_manage_conflicts).Execute();
            }
            catch (PathAlreadyAWorkingCopy)
            {
                Console.WriteLine("Path already a working copy");
                return;
            }
            catch (DiskSpaceException)
            {
                Console.WriteLine("Not enough disk space");
                return;
            }
            catch (UnauthorizedAccessException)
            {
                Console.WriteLine("Access denied");
                return;
            }
            catch (Exception)
            {
                Console.WriteLine("An unknown error has occurred.");
                return;
            }
			Console.WriteLine("Successfully synced {0} to {1}, and added sync config as {2}",path_to_folder, repos_url, new_sync_name);			
		}
		
		/// <summary>
		/// Usage: subsync.exe -s/--sync <sync_name> <auto_manage_conflicts: yes/no>
		/// </summary>
		/// <param name="tokens">
		/// A <see cref="Dictionary<System.String, List<System.Object>>"/>
		/// </param>
		public void executeSync (Dictionary<string, List<object>> tokens)
		{
			string sync_name = "";
			bool auto_manage_conflicts = true;
			
			if ((tokens["sync"].Count < 1) || (tokens["sync"].Count > 2))
			{
				Console.WriteLine ("Usage: subsync.exe -s/--sync <sync_name> <auto_manage_conflicts: yes/no>");
				return;			
			}
			
			sync_name = tokens["sync"][0].ToString();
			
			if (!SyncExists(sync_name)) {
				Console.WriteLine("Sync does not exist, please try again.");
				return;
			}
			
			if ((tokens["sync"].Count == 2)) {
				if (ValidBool(tokens["sync"][1].ToString()))
					auto_manage_conflicts = Tools.StringToBool(tokens["sync"][1].ToString());	
				else {
					Console.WriteLine("Please use only yes/no for conflict management switch.");
					return;
				}
			}

            try
            {
                Sync s = new Config().LoadSync(sync_name);
                if ((tokens["sync"].Count == 2)) s.auto_conflict_management = auto_manage_conflicts;
                s.Execute();
            }
            catch (SyncNotFound)
            {
                Console.WriteLine("Sync not found.");
                return;
            }
            catch (RepositoryNotFound)
            {
                Console.WriteLine("Repository not found.");
                return;
            }
            catch (DiskSpaceException)
            {
                Console.WriteLine("Not enough disk space");
                return;
            }
            catch (UnauthorizedAccessException)
            {
                Console.WriteLine("Access denied");
                return;
            }
            catch (Exception)
            {
                Console.WriteLine("An unknown error has occurred.");
                return;
            }
			Console.WriteLine("Successfully synced.");
		}

		/// <summary>
		/// Author: Hoang Duc
		/// </summary>
        /// <summary>
        /// Usage: subsync.exe -rs/--removeSync <sync_name> <auto_manage_conflicts ()optional override)>
        /// </summary>
        /// <param name="tokens">
        /// A <see cref="Dictionary<System.String, List<System.Object>>"/>
        /// </param>
        public void executeRemove(Dictionary<string, List<object>> tokens)
        {
            string sync_name = "";

            if ((tokens["removeSync"].Count != 1))
            {
                Console.WriteLine("Usage: subsync.exe -rs/--removeSync <sync_name>");
                return;
            }

            sync_name = tokens["removeSync"][0].ToString();

            if (!SyncExists(sync_name))
            {
                Console.WriteLine("Sync does not exist, please try again.");
                return;
            }

            try
            {
                Sync s = new Config().LoadSync(sync_name);
                s.Remove();
            }
            catch (SyncNotFound)
            {
                Console.WriteLine("Sync not found.");
                return;
            }
            catch (RepositoryNotFound)
            {
                Console.WriteLine("Repository not found.");
                return;
            }
            catch (UnauthorizedAccessException)
            {
                Console.WriteLine("Access denied");
                return;
            }
            catch (Exception)
            {
                Console.WriteLine("An unknown error has occurred.");
                return;
            }
            Console.WriteLine(sync_name + " successfully removed.");
        }

		/// <summary>
	    /// Author: Hoang Duc
	    /// </summary>
        /// <summary>
        /// Usage: subsync.exe -bt/--backupTransverse <sync_name> <revNo>
        /// </summary>
        /// <param name="tokens">
        /// A <see cref="Dictionary<System.String, List<System.Object>>"/>
        /// </param>
        public void executeBackupTransverse(Dictionary<string, List<object>> tokens)
        {
            if (tokens["backupTransverse"].Count != 2)
            {
                Console.WriteLine("Usage: subsync.exe -tt/--timetravel <sync_name> <revNo>");
                return;
            }

            string sync_name = tokens["backupTransverse"][0].ToString();
            string revisionNo = tokens["backupTransverse"][1].ToString();

            try
            {
                if (SyncExists(sync_name))
                {
                    Sync s = new Config().LoadSync(sync_name);
                    s.BackupTransverse(s.working_copy_path, revisionNo);
                }
                else
                {
                    Sync s = new Sync();
                    s.BackupTransverse(sync_name, revisionNo);
                }
            }
            catch (BackupTransverseException)
            {
                Console.WriteLine("Invalid sync name / path / revision");
                return;
            }
            catch (DiskSpaceException)
            {
                Console.WriteLine("Not enough disk space");
                return;
            }
            catch (UnauthorizedAccessException)
            {
                Console.WriteLine("Access denied");
                return;
            }
            catch (Exception)
            {
                Console.WriteLine("An unknown error has occurred.");
                return;
            }
        }

		public void Help (Dictionary<string, List<object>> tokens)
		{
			Console.WriteLine (@"
Setting up a new Sync:

 > subsync.exe 	--newsync/-n <path_to_folder> <sync-name: mp31> [auto-manage-conflicts?: yes/no] 
		[--customrepo/-cr <path_to_repo_storage_folder>]
		[--description/-ds <description>]
		[--icon/ic <path_to_icon>]

Adding a local folder to a sync:

> subsync.exe 	-al/--addlocalsync <path_to_folder> <source_sync_name> <new_sync_name> [(OPTIONAL) auto_manage_conflicts: yes/no]
		[--description/-ds <description>]
		[--icon/ic <path_to_icon>]

Adding a remote folder to a sync:

> subsync.exe 	-a/-addsync <path_to_folder> <svn_url> <new_sync_name> [auto_manage_conflicts: yes/no]
		[--description/-ds <description>]
		[--icon/ic <path_to_icon>]

Sync a folder:

> subsync.exe 	-s/--sync <sync_name> [auto_manage_conflicts: yes/no]

Time Travel:

> subsync.exe 	--timetravel/-tt <sync_name> <rev_no>

Deleting a Sync:

> subsync.exe 	--deletesync/-d <sync_name>
");
		}
		
		//Config TEST Views
		public void NewConfig (Dictionary<string, List<object>> tokens)
		{
			new Config ();
			Console.WriteLine ("Spawned new config object.");
		}
		
		public void FakeSyncSave (Dictionary<string, List<object>> tokens)
		{
			Sync s = new Sync ();
			s.name = "asd";
			s.repo_url = "/root";
			s.working_copy_path = "/etc";
			s.auto_conflict_management = true;
			s.hosted = true;
			
			new Config().SaveSync(s);
			
			Console.WriteLine ("Saved fake sync.");
		}
	
		public void FakeSyncDel (Dictionary<string, List<object>> tokens)
		{
			Sync s = new Sync ();
			s.name = "asd";
			s.repo_url = "/root";
			s.working_copy_path = "/etc";
			
			new Config().DeleteSync(s);
			Console.WriteLine ("Deleted fake sync.");
		}

		public void FakeSyncLoad (Dictionary<string, List<object>> tokens)
		{
			Sync s = new Config ().LoadSync ("asd");
			Console.WriteLine ("Name: {0}", s.name);
			Console.WriteLine ("RepoURL: {0}", s.repo_url);
			Console.WriteLine ("WorkingCopyPath: {0}", s.working_copy_path);
		}
		
		public bool ValidPath (string path)
		{
			try {
				Path.GetDirectoryName (path);
			}
			catch (System.Exception) {
				return false;
			}
			return true;
		}
		
		public bool ValidBool (string s)
		{
			if ((s == "yes") || (s == "no"))
				return true;
            else
                return false;
		}
		
		public bool SyncExists (string s)
		{
			try {
				new Config ().LoadSync (s);
			} catch (SyncNotFound) {
				return false;
			}
			return true;
		}
	}
}
